package com.wongoing.apiclient.weixin;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import com.wongoing.bus.util.Constants;
import com.wongoing.util.DateConvert;
import com.wongoing.util.DateStyle;
import com.wongoing.util.IpUtil;
import com.wongoing.util.NumberUtil;
import com.wongoing.util.UrlConstants;
import com.wongoing.util.safe.rsa.RSAHelper;

import cn.jiguang.common.utils.StringUtils;
import io.netty.util.internal.StringUtil;


/**
 * 功能说明：此类封装微信支付的统一调用接口
 * 修改说明：
 * @author 吴兆强
 * @date 2018年6月19日 下午10:58:34
 * @version V0.1
 */
public class WXPayHelper {

	private static WXPayHelper instance;
	private WXPay wxpay;
    private WXPayConfigImpl config;
    
	public WXPayHelper() {
		super();
	}
	
	//单例模式
	public synchronized static WXPayHelper getInstance() {
		if(instance==null) {
			instance = new WXPayHelper();
		}
		return instance;
	}
	
	/**
	 * 功能说明：微信WAP H5页面支付接口（移动端网页调起）
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2018年3月4日 下午8:41:41
	 * @param openid
	 * @param out_trade_no
	 * @param total_fee
	 * @param body
	 * @param product_id
	 * @param ip
	 * @return
	 * @throws Exception
	 */
	public Map<String, String> weixinWapH5PayResult(String out_trade_no, String total_fee, String body, String product_id, String ip) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config,config.getNotifyUrl(),false,false);
	    HashMap<String, String> data = new HashMap<String, String>();
        data.put("body", body);
        data.put("out_trade_no", out_trade_no);
        data.put("total_fee", total_fee);
        data.put("spbill_create_ip", ip);
        data.put("product_id", product_id);
        data.put("trade_type", config.getTradeTypeMWEB());
        data.put("device_info", config.getDeviceInfo());
        data.put("fee_type", config.getFeeType());
        data.put("time_expire", DateConvert.formatDateToString(DateConvert.addMinute(new Date(), 10), DateStyle.YYYYMMDDHHMMSS));
		return wxpay.unifiedOrder(data);
	} 
	
	/**
	 * 功能说明：微信公众号支付接口（项目调用）
	 * 修改说明：要以失效时间为10min
	 * @author 吴兆强
	 * @date 2017年12月18日 上午11:38:40
	 * @param body 商品描述
	 * @param product_id 商品id（扫码必传参数）
	 * @param ip 终端ip地址
	 * @param out_trade_no 自定义订单号
	 * @param total_fee 订单金额
	 * @return 返回map 参数
	 * @throws Exception
	 */
	public Map<String, String> weixinH5PayResult(String openid, String out_trade_no, String total_fee, String body, String product_id, String ip) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config,config.getNotifyUrl(),false,false);
	    HashMap<String, String> data = new HashMap<String, String>();
        data.put("body", body);
        data.put("out_trade_no", out_trade_no);
        data.put("total_fee", total_fee);
        data.put("spbill_create_ip", ip);
        data.put("product_id", product_id);
        data.put("trade_type", config.getTradeTypeJSAPI());
        data.put("device_info", config.getDeviceInfo());
        data.put("fee_type", config.getFeeType());
        data.put("openid", openid);
        data.put("time_expire", DateConvert.formatDateToString(DateConvert.addMinute(new Date(), 10), DateStyle.YYYYMMDDHHMMSS));
		return wxpay.unifiedOrder(data);
	} 
	
	/**
	 * 功能说明：微信支付接口（项目调用）
	 * 修改说明：要以失效时间为10min
	 * @author 吴兆强
	 * @date 2017年12月18日 上午11:38:40
	 * @param body 商品描述
	 * @param product_id 商品id（扫码必传参数）
	 * @param ip 终端ip地址
	 * @param out_trade_no 自定义订单号
	 * @param total_fee 订单金额
	 * @return 返回map 参数
	 * @throws Exception
	 */
	public Map<String, String> weixinPayResult( String out_trade_no, String total_fee, String body, String product_id, String ip) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config,config.getNotifyUrl(),false,false);
	    HashMap<String, String> data = new HashMap<String, String>();
        data.put("body", body);
        data.put("out_trade_no", out_trade_no);
        data.put("total_fee", total_fee);
        data.put("spbill_create_ip", ip);
        data.put("product_id", product_id);
        data.put("trade_type", config.getTradeTypeNative());
        data.put("device_info", config.getDeviceInfo());
        data.put("fee_type", config.getFeeType());
        data.put("time_expire", DateConvert.formatDateToString(DateConvert.addMinute(new Date(), 10), DateStyle.YYYYMMDDHHMMSS));
		return wxpay.unifiedOrder(data);
	
	} 
	/**
	 * 功能说明：微信支付订单查询接口（传入商户自定义订单号）
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2017年12月25日 下午9:30:18
	 * @param out_trade_no 商户订单号（唯一）
	 * @return <xml>
	 *	 		    <return_code><![CDATA[SUCCESS]]></return_code>
	 *	 	        <return_msg><![CDATA[OK]]></return_msg>
	 *			    <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
	 *			    <mch_id><![CDATA[10000100]]></mch_id>
	 *			    <device_info><![CDATA[1000]]></device_info>
	 *			    <nonce_str><![CDATA[TN55wO9Pba5yENl8]]></nonce_str>
	 *			    <sign><![CDATA[BDF0099C15FF7BC6B1585FBB110AB635]]></sign>
	 *			    <result_code><![CDATA[SUCCESS]]></result_code>
	 *			    <openid><![CDATA[oUpF8uN95-Ptaags6E_roPHg7AG0]]></openid>
	 *			    <is_subscribe><![CDATA[Y]]></is_subscribe>
	 *			    <trade_type><![CDATA[MICROPAY]]></trade_type>
	 *			    <bank_type><![CDATA[CCB_DEBIT]]></bank_type>
	 *			    <total_fee>1</total_fee>
	 *			    <fee_type><![CDATA[CNY]]></fee_type>
	 *			    <transaction_id><![CDATA[1008450740201411110005820873]]></transaction_id>
	 *			    <out_trade_no><![CDATA[1415757673]]></out_trade_no>
	 *			    <attach><![CDATA[订单额外描述]]></attach>
	 *			    <time_end><![CDATA[20141111170043]]></time_end>
	 *			    <trade_state><![CDATA[SUCCESS]]></trade_state>
	 *			 </xml> 
	 * @throws Exception
	 */
	public Map<String, String> weixinPayQuery(String out_trade_no) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config);
	    HashMap<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", out_trade_no);
		return wxpay.orderQuery(data);
	} 
	
	/**
	 * 功能说明：微信支付查询接口（传入微信交易号）
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2017年12月25日 下午9:31:43
	 * @param transaction_id
	 * @return <xml>
	 *	 		    <return_code><![CDATA[SUCCESS]]></return_code>
	 *	 	        <return_msg><![CDATA[OK]]></return_msg>
	 *			    <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
	 *			    <mch_id><![CDATA[10000100]]></mch_id>
	 *			    <device_info><![CDATA[1000]]></device_info>
	 *			    <nonce_str><![CDATA[TN55wO9Pba5yENl8]]></nonce_str>
	 *			    <sign><![CDATA[BDF0099C15FF7BC6B1585FBB110AB635]]></sign>
	 *			    <result_code><![CDATA[SUCCESS]]></result_code>
	 *			    <openid><![CDATA[oUpF8uN95-Ptaags6E_roPHg7AG0]]></openid>
	 *			    <is_subscribe><![CDATA[Y]]></is_subscribe>
	 *			    <trade_type><![CDATA[MICROPAY]]></trade_type>
	 *			    <bank_type><![CDATA[CCB_DEBIT]]></bank_type>
	 *			    <total_fee>1</total_fee>
	 *			    <fee_type><![CDATA[CNY]]></fee_type>
	 *			    <transaction_id><![CDATA[1008450740201411110005820873]]></transaction_id>
	 *			    <out_trade_no><![CDATA[1415757673]]></out_trade_no>
	 *			    <attach><![CDATA[订单额外描述]]></attach>
	 *			    <time_end><![CDATA[20141111170043]]></time_end>
	 *			    <trade_state><![CDATA[SUCCESS]]></trade_state>
	 *			 </xml> 
	 * @throws Exception
	 */
	public Map<String, String> weixinPayQueryById(String transaction_id) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config);
	    HashMap<String, String> data = new HashMap<String, String>();
        data.put("transaction_id", transaction_id);
		return wxpay.orderQuery(data);
	} 
	
	/**
	 * 功能说明：微信退款
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2017年12月25日 下午10:04:24
	 * @param out_trade_no
	 * @param out_refund_no
	 * @param total_fee
	 * @param refund_fee
	 * @param refund_fee_type
	 * @return
	 * @throws Exception
	 */
	public Map<String, String> weixinPayRefund(String out_trade_no,
			String out_refund_no,
			String total_fee,
			String refund_fee) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config);
		HashMap<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", out_trade_no);
        data.put("out_refund_no", out_refund_no);
        data.put("total_fee", total_fee);
        data.put("refund_fee", refund_fee);
        data.put("refund_fee_type", "CNY");
        data.put("op_user_id", config.getMchID());
        return wxpay.refund(data);
	}
	
	/**
	 * 功能说明：微信退款查询
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2018年2月24日 上午11:04:36
	 * @param out_refund_no
	 * @return
	 * @throws Exception
	 */
	public Map<String, String> weixinPayRefundQuery(String out_refund_no) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config);
		HashMap<String, String> data = new HashMap<String, String>();
        data.put("out_refund_no", out_refund_no);
        return wxpay.refundQuery(data);
	}
	
	/**
	 * 功能说明：微信转账到银行卡
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2018年6月26日 下午10:45:35
	 * @param partner_trade_no 商户企业付款单号
	 * @param enc_bank_no 收款方银行卡号
	 * @param enc_true_name 收款方用户名
	 * @param bank_code 收款方开户行
	 * @param amount 付款金额
	 * @param desc 付款说明
	 * @return
	 * @throws Exception
	 */
	public Map<String, String> weixinTransferResult(String partner_trade_no,
			String enc_bank_no,
			String enc_true_name,
			String bank_code, 
			String amount, 
			String desc) throws Exception{
		config = WXPayConfigImpl.getInstance();
	    wxpay = new WXPay(config);
		HashMap<String, String> data = new HashMap<String, String>();
		enc_bank_no = RSAHelper.getInstance().decrypt(enc_bank_no);
		enc_true_name = RSAHelper.getInstance().decrypt(enc_true_name);
        data.put("partner_trade_no", partner_trade_no);
        data.put("enc_bank_no", enc_bank_no);
        data.put("enc_true_name", enc_true_name);
        data.put("bank_code", bank_code);
        data.put("amount", amount);
        if(!org.apache.commons.lang3.StringUtils.isBlank(desc)) 
        	  data.put("desc", desc);
        return wxpay.transferOrder(data);
	}
	
	/**
	 * 功能说明：微信通知及校验失败后，返回错误信息到微信服务器（xml）
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2017年12月19日 下午2:11:59
	 * @return
	 * @throws Exception
	 */
	public String wxPayFailToXml(String msg) throws Exception {
		Map<String,String> resultToWX = new HashMap<String, String>();
		resultToWX.put("return_code", "FAIL");
		if(null != msg) resultToWX.put("return_msg", msg);
		return WXPayUtil.mapToXml(resultToWX);
	}
	
	/**
	 * 功能说明：微信通知及校验成功后，返回错误信息到微信服务器（xml）
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2017年12月19日 下午2:15:00
	 * @return
	 * @throws Exception
	 */
	public String wxPaySuccessToXml() throws Exception {
		Map<String,String> resultToWX = new HashMap<String, String>();
		resultToWX.put("return_code", "SUCCESS");
		resultToWX.put("return_msg", "OK");
		return WXPayUtil.mapToXml(resultToWX);
	}
	
	/**
	 * 功能说明：微信H5支付获取mweb_url后二次请求，直接调起APP
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2018年6月19日 上午11:35:02
	 * @param path
	 * @param ip
	 * @return
	 */
	public String getRedirectInfo(String path, String ip) {
		HttpClient httpClient = new DefaultHttpClient();
		HttpContext httpContext = new BasicHttpContext();
		HttpGet httpGet = new HttpGet(path);
		httpGet.setHeader("Referer", config.getReferer());//
		httpGet.setHeader("x-forwarded-for", ip);
		String html = "";
		try {
			// 将HttpContext对象作为参数传给execute()方法,则HttpClient会把请求响应交互过程中的状态信息存储在HttpContext中
			HttpResponse response = httpClient.execute(httpGet, httpContext);
			// 获取重定向之后的主机地址信息,即"http://127.0.0.1:8088"
			HttpHost targetHost = (HttpHost) httpContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
			// 获取实际的请求对象的URI,即重定向之后的"/blog/admin/login.jsp"
			HttpUriRequest realRequest = (HttpUriRequest) httpContext.getAttribute(ExecutionContext.HTTP_REQUEST);
			//System.out.println("主机地址:" + targetHost);
			//System.out.println("URI信息:" + realRequest.getURI());
			HttpEntity entity = response.getEntity();
			if (null != entity) {
				// System.out.println("响应内容:" + EntityUtils.toString(entity,
				// ContentType.getOrDefault(entity).getCharset()));
				html = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
				EntityUtils.consume(entity);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			httpClient.getConnectionManager().shutdown();
		}
		return html;
	}
	
	/**
	 * 功能说明：微信H5预支付返回结果后二次请求调起APP数据，并使用Deeplink解析，
	 * 修改说明：
	 * @author 吴兆强
	 * @date 2018年6月19日 上午11:33:11
	 * @param WxPayMap
	 * @param ip
	 * @return
	 */
	public String goWxPayDeeplinkStr(Map<String, String> WxPayMap, String ip){
		String mwebUrl = WxPayMap.get("mweb_url") + "";
		String html = getRedirectInfo(mwebUrl, ip);
		//使用正则表达式截取所需数据
		Matcher m = Pattern.compile("(weixin.+?;)").matcher(html);
		if(!m.find()) 
			return null;
		String deeplink = m.group(0).replaceAll("\"", "").replace(";", "");
		return deeplink;
	};
	
}
